﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO;
using System.Text;
using System.ComponentModel;
using LetiLib.Utils;

namespace LetiLib.Data
{
    public enum HASH_TAG
    {
        [Description("#e0")]
        EFFECT_STATIC = 0x00,
        [Description("#e1")]
        EFFECT_TOPDOWN,
        [Description("#e2")]
        EFFECT_BOTTOMUP,
        [Description("#e3")]
        EFFECT_LEFTRIGHT,
        [Description("#e4")]
        EFFECT_RIGHTLEFT,

        [Description("#cr")]
        COLOR_RED,
        [Description("#cg")]
        COLOR_GREEN,
        [Description("#cy")]
        COLOR_YELLOW,

        [Description("#f0")]
        FONT_SMALL,
        [Description("#f1")]
        FONT_MEDIUM,
        [Description("#f2")]
        FONT_LARGE,

        [Description("#al")]
        ALIGN_LEFT,
        [Description("#ar")]
        ALIGN_RIGHT,
        [Description("#ac")]
        ALIGN_CENTER,

        

        [Description("#n")]
        NEWLINE,
        /*
         * Temporary NOT use
        [Description("#date")]
        CALENDAR_DATE,
        [Description("#time")]
        CALENDAR_TIME
         * */
    }
    public struct TABLE
    {

        public byte Rows;
        public byte Columns;
        public char SplitSymbol;
        public string ContentData;

        public const int TABLE_HEADER_LENGTH = 5;

        public TABLE(byte row, byte colum, char symbol,string data)
        {
            Rows = row;
            Columns = colum;
            SplitSymbol = symbol;
            if (data == null)
                ContentData = "";
            else
                ContentData = data;
        }

        /// <summary>
        /// Return raw data string without flags
        /// </summary>
        /// <returns> raw string</returns>
        public string getRawDataString()
        {
            if (ContentData == null)
            {
                return string.Empty;
            }
            string rawString = (string)ContentData.Clone();
            foreach (HASH_TAG tag in EnumUtils.EnumToList<HASH_TAG>())
            {
                string tagString = EnumUtils.GetEnumDescription(tag);
                rawString = rawString.Replace(tagString, "");
            }
            return rawString;
        }

        public byte[] getBytes()
        {
            if (ContentData != null && ContentData.Split(SplitSymbol).Length == (Rows * Columns))
            {
                byte[] buffer = new byte[TABLE_HEADER_LENGTH + ContentData.Length];
                buffer[0] = Rows;
                buffer[1] = Columns;
                buffer[2] = (byte)SplitSymbol;

                System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
                BitConverter.GetBytes((ushort)ContentData.Length).CopyTo(buffer, 3);
                encoding.GetBytes(ContentData).CopyTo(buffer, 5);               

                return buffer;
            }
            else
                return null;
        }
    }

    public struct IMAGE
    {
        public byte PlayOrder;
        public ushort ImageCode;// convert to int

        public ushort Delay;
        public EFFECT Effect;
        public const int IMAGE_LENGTH = 6;// update length

        public IMAGE(byte pOrder,ushort imgCode,ushort delay,EFFECT effect)
        {
            PlayOrder = pOrder;
            ImageCode = imgCode;
            Delay = delay;
            Effect = effect;
        }

        public byte[] getBytes()
        {
            byte[] buffer = new byte[IMAGE_LENGTH];
            buffer[0] = PlayOrder;
            BitConverter.GetBytes(ImageCode).CopyTo(buffer, 1);
            BitConverter.GetBytes(Delay).CopyTo(buffer, 3);
            buffer[5] = (byte)Effect;
            return buffer;
        }
    }                        

    public enum EFFECT                                                                                                                                                       
    {
        [Description("#e0")]STATIC = 0x00,
        [Description("#e1")]TOPDOWN,
        [Description("#e2")]BOTTOMUP,
        [Description("#e3")]LEFTRIGHT,
        [Description("#e4")]RIGHTLEFT,
    }

    public struct DATETIME
    {
        byte year;
        byte month;
        byte day;
        byte hour;
        byte minute;
        byte sec;
        public const int DATETIME_LENGTH = 6;

        public static DATETIME FromBytes(byte[] bytes)
        {
            if (bytes.Length != DATETIME_LENGTH) return new DATETIME();

            DATETIME dt = new DATETIME();
            
            dt.year = bytes[0];
            dt.month = bytes[1];
            dt.day = bytes[2];
            dt.hour = bytes[3];
            dt.minute = bytes[4];
            dt.sec = bytes[5];
            return dt;
        }

        public static DATETIME FromChars(char[] chars)
        {
            if (chars.Length != DATETIME_LENGTH) return new DATETIME();

            DATETIME dt = new DATETIME();

            dt.year = (byte)(chars[0] - '0');
            dt.month = (byte)(chars[1] - '0');
            dt.day = (byte)(chars[2] - '0');
            dt.hour = (byte)(chars[3] - '0');
            dt.minute = (byte)(chars[4] - '0');
            dt.sec = (byte)(chars[5] - '0');
            return dt;
        }

        public byte[] getBytes()
        {
            byte[] buffer = new byte[DATETIME_LENGTH];

            buffer[0] = year;
            buffer[1] = month;
            buffer[2] = day;
            buffer[3] = hour;
            buffer[4] = minute;
            buffer[5] = sec;

            return buffer;
        }
    }
    //[StructLayout(LayoutKind.Sequential, Pack = 1)]
    public abstract class POPUP
    {
        public ushort ID;
        public ushort X;
        public ushort Y;
        public byte Z;
        public ushort Width;
        public ushort Height;
        public POPUP_TYPE DataType;
        public DATETIME StartDate;
        public DATETIME EndDate;
        public byte ItemCount;
        public byte ActiveItem;
        public ushort _reversed;

        public const int POPUP_HEADER_SIZE = 28;

        public abstract byte[] getBytes();
    }

    // popup with no data
    public class GENERAL_POPUP :POPUP
    {
        public override byte[] getBytes()
        {
            byte[] buffer = null;
            buffer = new byte[POPUP_HEADER_SIZE];

            if (buffer == null) return null;
            int offset = 0;
            BitConverter.GetBytes(ID).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(X).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Y).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Z).CopyTo(buffer, offset);

            offset += sizeof(byte);
            BitConverter.GetBytes(Width).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Height).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            buffer[offset] = (byte)DataType;

            offset += sizeof(byte);
            buffer[offset] = ItemCount;

            offset += 1;
            buffer[offset] = ActiveItem;

            offset += 1;
            buffer[offset] = 0x00;// Reversed 1
            buffer[offset + 1] = 0x05;// Reversed 2

            offset += 2;
            StartDate.getBytes().CopyTo(buffer, offset);

            offset += DATETIME.DATETIME_LENGTH;
            EndDate.getBytes().CopyTo(buffer, offset);
            return buffer;
        }
    }

    public class TABLE_POPUP : POPUP
    {
        public TABLE Table;

        public override byte[] getBytes()
        {
            byte[] buffer = null;

            if (DataType == POPUP_TYPE.TABLE && Table.getBytes() != null)
            {
                buffer = new byte[POPUP_HEADER_SIZE + Table.getBytes().Length];
            }

            if (buffer == null) return null;
            int offset = 0;
            BitConverter.GetBytes(ID).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(X).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Y).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Z).CopyTo(buffer, offset);

            offset += sizeof(byte);
            BitConverter.GetBytes(Width).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Height).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            buffer[offset] = (byte)DataType;

            offset += sizeof(byte);
            buffer[offset] = ItemCount;

            offset += 1;
            buffer[offset] = ActiveItem;

            offset += 1;
            buffer[offset] = 0x00;// Reversed 1
            buffer[offset + 1] = 0x05;// Reversed 2

            offset += 2;
            StartDate.getBytes().CopyTo(buffer, offset);

            offset += DATETIME.DATETIME_LENGTH;
            EndDate.getBytes().CopyTo(buffer, offset);

            offset += DATETIME.DATETIME_LENGTH;
            if (DataType == POPUP_TYPE.TABLE && Table.getBytes() != null)
            {
                Table.getBytes().CopyTo(buffer, offset);
            }
            return buffer;
        }
    }

    public class PLAYLIST_POPUP :POPUP
    {
        /*
        public PLAYLIST_POPUP():base()
        {
            Images = new List<IMAGE>();
        }
        */
        /*
        public PLAYLIST_POPUP(POPUP p)
        {
            ID = p.ID;
            X = p.X;
            Y = p.Y;
            Z = p.Z;
            Width = p.Width;
            Height = p.Height;
            DataType = p.DataType;
            StartDate = p.StartDate;
            EndDate = p.EndDate;
            ItemCount = p.ItemCount;
            ActiveItem = p.ActiveItem;

            Images = new List<IMAGE>();
        }
        */
        public List<IMAGE> Images;

        public override byte[] getBytes()
        {
            byte[] buffer = null;

            buffer = new byte[POPUP_HEADER_SIZE + ItemCount * IMAGE.IMAGE_LENGTH];

            if (buffer == null) return null; ;
            int offset = 0;
            BitConverter.GetBytes(ID).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(X).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Y).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Z).CopyTo(buffer, offset);

            offset += sizeof(byte);
            BitConverter.GetBytes(Width).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Height).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            buffer[offset] = (byte)DataType;

            offset += sizeof(byte);
            buffer[offset] = ItemCount;

            offset += 1;
            buffer[offset] = ActiveItem;

            offset += 1;
            buffer[offset] = 0x00;// Reversed 1
            buffer[offset + 1] = 0x05;// Reversed 2

            offset += 2;
            StartDate.getBytes().CopyTo(buffer, offset);

            offset += DATETIME.DATETIME_LENGTH;
            EndDate.getBytes().CopyTo(buffer, offset);

            offset += DATETIME.DATETIME_LENGTH;
            if (Images != null && Images.Count > 0 && DataType == POPUP_TYPE.PLAYLIST)
            {
                int index = 0;
                foreach (IMAGE img in Images)
                {
                    if (index < ItemCount)
                    {
                        img.getBytes().CopyTo(buffer, offset);
                        offset += IMAGE.IMAGE_LENGTH;
                        index++;
                    }
                }
            }
            return buffer;
        }
    
    }

    public class TEXT_POPUP : POPUP
    {
        public String Content = string.Empty;

        public override byte[] getBytes()
        {
            byte[] buffer = null;
            // Data format:
            // <Header> <length> <string data>
            // <header> 28 bytes header popup
            // <length> string content's length
            // <string data> decoded string data
            buffer = new byte[POPUP_HEADER_SIZE + Content.Length + 2];

            if (buffer == null) return null;
            int offset = 0;
            BitConverter.GetBytes(ID).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(X).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Y).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Z).CopyTo(buffer, offset);

            offset += sizeof(byte);
            BitConverter.GetBytes(Width).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Height).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            buffer[offset] = (byte)DataType;

            offset += sizeof(byte);
            buffer[offset] = ItemCount;

            offset += 1;
            buffer[offset] = ActiveItem;

            offset += 1;
            buffer[offset] = 0x00;// Reversed 1
            buffer[offset + 1] = 0x05;// Reversed 2

            offset += 2;
            StartDate.getBytes().CopyTo(buffer, offset);

            offset += DATETIME.DATETIME_LENGTH;
            EndDate.getBytes().CopyTo(buffer, offset);

            offset += DATETIME.DATETIME_LENGTH;
            BitConverter.GetBytes((ushort)Content.Length).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            StringUtils.ConvertText(Content).CopyTo(buffer, offset);
           
            return buffer;
        }


    }

    public enum POPUP_TYPE
    {
        UNKNOW = 0x00,
        PLAYLIST ,
        CLOCK,
        TEMP,
        TABLE,
        TEXT
    }

    public enum EVENT_TYPE
    {
        NORMAL,
        DEFAULT_PAGE,
        PREDEFINED_PAGE,
        EMERGENCY_PAGE
    }

    public struct LETIPageHeader
    {
        public ushort ID;
        public byte ItemCount;
        public EVENT_TYPE EventType;
    }

    [Serializable]
    public class LETIPage
    {
        public LETIPageHeader Header;

        private List<POPUP> _PopUps;
        public List<POPUP> PopUps { get { return _PopUps; } set { _PopUps = value; } }
        public const int PAGE_HEADER_SIZE = 4;
        private int iBufferSize ;
        
        public int BufferSize { get { return iBufferSize; } }
        public LETIPage()
        {
            Header.ID = 0;
            Header.ItemCount = 0;
            Header.EventType = 0;
            iBufferSize = PAGE_HEADER_SIZE;
        }

        public LETIPage(ushort id,byte itemCount = 0,EVENT_TYPE @event = 0)
        {
            Header.ID = id;
            Header.ItemCount = itemCount;
            Header.EventType = @event;
            iBufferSize = PAGE_HEADER_SIZE;
        }

        public LETIPage(byte[] btReceive)
        {
            // TODO: Complete member initialization
            Header.ID = (ushort)BitConverter.ToInt16(btReceive, 4);
            Header.ItemCount = btReceive[6];
            Header.EventType = (EVENT_TYPE)Enum.Parse(typeof(EVENT_TYPE),btReceive[7].ToString());
            int index = 8;
            if (Header.ItemCount > 0)
                _PopUps = new List<POPUP>();

            for (int i = 0; i < Header.ItemCount; i++)
            {
                //POPUP p = new POPUP();
                
                ushort ID = BitConverter.ToUInt16(btReceive, index);
                index += sizeof(ushort);

                ushort X = BitConverter.ToUInt16(btReceive, index);
                index += sizeof(ushort);


                ushort Y = BitConverter.ToUInt16(btReceive, index);
                index += sizeof(ushort);

                byte Z = btReceive[index];
                index += sizeof(byte);

                ushort Width = BitConverter.ToUInt16(btReceive, index);
                index += sizeof(ushort);

                ushort Height = BitConverter.ToUInt16(btReceive, index);
                index += sizeof(ushort);

                POPUP_TYPE Type = (POPUP_TYPE)btReceive[index];
                index += sizeof(byte);

                byte ItemCount = btReceive[index];
                index += sizeof(byte);

                byte ActiveItem = btReceive[index];
                index += sizeof(byte);

                // Create popup object
                POPUP p = EnumUtils.getPopupByType(Type);
                p.ID = ID;
                p.X = X;
                p.Y = Y;
                p.Z = Z;
                p.Width = Width;
                p.Height = Height;
                p.ItemCount = ItemCount;
                p.ActiveItem = ActiveItem;
                //p.StartDate = (btReceive index);
                unsafe
                {
                    index += sizeof(DATETIME) * 2 +2;
                }
                if (p.GetType() == typeof(PLAYLIST_POPUP))
                {
                    ((PLAYLIST_POPUP)p).Images = new List<IMAGE>();

                    for (int j = 0; j < p.ItemCount; j++)
                    {
                        IMAGE img = new IMAGE();

                        img.PlayOrder = btReceive[index];
                        index += sizeof(byte);

                        img.ImageCode = BitConverter.ToUInt16(btReceive, index);
                        index += sizeof(ushort);

                        img.Delay = BitConverter.ToUInt16(btReceive, index);
                        index += sizeof(ushort);

                        img.Effect = (EFFECT)btReceive[index];
                        index += sizeof(byte);

                        ((PLAYLIST_POPUP)p).Images.Add(img);
                    }
                }

                _PopUps.Add(p);
            }
        }

        public void AddPopup(POPUP p)
        {
            int popUpSize = 0;
            if (p.GetType() == typeof(TABLE_POPUP) && ((TABLE_POPUP)p).Table.getBytes() != null)
            {
                popUpSize = ((TABLE_POPUP)p).getBytes().Length;
            }
            else if (p.GetType() == typeof(TEXT_POPUP))
            {
                popUpSize = ((TEXT_POPUP)p).getBytes().Length;
            }
            else if (p.GetType() == typeof(PLAYLIST_POPUP))
            {
                popUpSize =  ((PLAYLIST_POPUP)p).getBytes().Length;
            }
            else
            {
                popUpSize = POPUP.POPUP_HEADER_SIZE;
            }
            
            iBufferSize += popUpSize;
            if (_PopUps == null)
                _PopUps = new List<POPUP>();

            _PopUps.Add(p);
            //this.Header.ItemCount++;
        }


        public byte[] getBytes()
        {
            iBufferSize = PAGE_HEADER_SIZE;
            List<byte[]> datas = null;
            if (_PopUps != null)
            {
                datas = new List<byte[]>();
                foreach (POPUP p in _PopUps)
                {
                    byte[] tempBuf = p.getBytes();
                    datas.Add(tempBuf);
                    iBufferSize += tempBuf.Length;
                }
            }
            byte[] buffer = new byte[iBufferSize];
            using (var fileStream = new MemoryStream(buffer))
            using (var bw = new BinaryWriter(fileStream))
            {
                bw.Write(Header.ID);
                bw.Write(Header.ItemCount);
                bw.Write((byte)Header.EventType);
                if (datas != null)
                {
                    foreach (byte[] data in datas)
                    {
                        bw.Write(data);
                    }
                }
            }
            return buffer;
        }
         
        public void save(string path)
        {
            File.WriteAllBytes(path,getBytes());
            return;
        }

        public override string ToString()
        {
            StringBuilder result = new StringBuilder();

            result.AppendFormat("{0,20} {1}\n", "Page's ID:",  Header.ID);
            result.AppendFormat("{0,20} {1}\n", "Item Count:", Header.ItemCount);
            result.AppendFormat("{0,20} {1}\n", "Event Type:", Header.EventType);
            if (PopUps != null)
            {
                foreach (POPUP p in PopUps)
                {
                    result.Append("***********\n");
                    result.AppendFormat("-----{0,15} {1}\n", "Popup ID:", p.ID);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup X:", p.X);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup Y:", p.Y);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup Z:", p.Z);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup Width:", p.Width);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup Height:", p.Height);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup Type:", p.DataType.ToString());
                }
            }
            return result.ToString();
        }
    }
}